package com.samtech.common.utils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EmbeddedId;
import javax.persistence.Id;



public final class ClassUtils {
	private static Map<Class,AnalyzeResult> map=new HashMap<Class, AnalyzeResult>(20);
	
 public static AnalyzeResult getAnalyzeResult(Class clazz){
	 if(map.containsKey(clazz))return map.get(clazz);
	 else{
		 analyze(clazz);
		 if(map.containsKey(clazz))return map.get(clazz);
	 }
	 return null;
 }
 protected synchronized static void analyze(Class clazz){
	 Map<String,Method> readMethod=new HashMap<String,Method>(20);
	 Map<String,Method> writeMethod=new HashMap<String,Method>(20);
	 String idField=null;
	 Class<?> idClass=null;
	 String[] idFieldNames=null;
	 List<String> fieldNames=new ArrayList<String>(20);
	 Method[] methods = clazz.getMethods();
	 Class<?> returnType=null;
	
	 for (int i = 0; i < methods.length; i++) {
		Method method = methods[i];
		boolean idm= method.isAnnotationPresent(Id.class)||method.isAnnotationPresent(EmbeddedId.class);
		
		if(method.getName().startsWith("get")){
			
			String f = method.getName().replaceFirst("get", "").trim();
			returnType = method.getReturnType();
			f=f.substring(0, 1).toLowerCase()+f.substring(1);
			if(!fieldNames.contains(f)) fieldNames.add(f);
			readMethod.put(f, method);
			if(idm)idField=f;
		}
		if(method.getName().startsWith("set")){
			
			String f = method.getName().replaceFirst("set", "").trim();
			Class<?>[] parameterTypes = method.getParameterTypes();
			if(parameterTypes!=null && parameterTypes.length==1)
				returnType=parameterTypes[0];
			f=f.substring(0, 1).toLowerCase()+f.substring(1);
			if(!fieldNames.contains(f)) fieldNames.add(f);
			writeMethod.put(f, method);
			if(idm)idField=f;
		}
		if(method.getName().startsWith("is")){
			
			String f = method.getName().replaceFirst("is", "").trim();
			returnType = method.getReturnType();
			if(returnType.equals(Boolean.class)|| boolean.class.equals(returnType)){
			f=f.substring(0, 1).toLowerCase()+f.substring(1);
			if(!fieldNames.contains(f)) fieldNames.add(f);
				readMethod.put(f, method);
			}
			if(idm)idField=f;
		}
	}
	if(idField!=null){
		boolean isMult=true;
		if(readMethod.containsKey(idField)){
			Method method = readMethod.get(idField);
			Class<?> t = method.getReturnType();
			idClass=t;
			if(t.isPrimitive()){
				isMult=false;
			}else{
				String name = t.getName();
				if(name.indexOf("java.lang")>=0){
					isMult=false;
				}
				else if(name.indexOf(BigDecimal.class.getName())>=0|| name.indexOf(BigInteger.class.getName())>=0|| name.indexOf(BigInteger.class.getName())>=0){
					isMult=false;
				}
			}
			if(isMult){
				analyze(t);
				if(map.containsKey(t)){
					AnalyzeResult analyzeResult = map.get(t);
					List<String> fldns = analyzeResult.getFieldNames();
					if(fldns!=null && !fldns.isEmpty()){
						idFieldNames=new String[fldns.size()];
						idFieldNames=fldns.toArray(idFieldNames);
					}
				}
			}
			
		}else if(writeMethod.containsKey(idField)){
			Method method = writeMethod.get(idField);
			 Class<?>[] types = method.getParameterTypes();
			 Class<?> t =null;
			 if(types!=null && types.length==1)t=types[0];
			 if(t!=null){
				 idClass=t;
				if(t.isPrimitive()){
					isMult=false;
				}else{
					String name = t.getName();
					if(name.indexOf("java.lang")>=0){
						isMult=false;
					}
					else if(name.indexOf(BigDecimal.class.getName())>=0|| name.indexOf(BigInteger.class.getName())>=0|| name.indexOf(BigInteger.class.getName())>=0){
							isMult=false;
					}
				}
				if(isMult){
					analyze(t);
					if(map.containsKey(t)){
						AnalyzeResult analyzeResult = map.get(t);
						List<String> fldns = analyzeResult.getFieldNames();
						if(fldns!=null && !fldns.isEmpty()){
							idFieldNames=new String[fldns.size()];
							idFieldNames=fldns.toArray(idFieldNames);
						}
					}
				}
			 }
		}
	}else{
		Field[] fields = clazz.getFields();
		boolean isMult=true;
		if(fields!=null && fields.length>0){
			for (int i = 0; i < fields.length; i++) {
				Field field = fields[i];
				if(field.isAnnotationPresent(Id.class)||field.isAnnotationPresent(EmbeddedId.class)){
					Class<?> t = field.getType();
					idClass=t;
					idField=field.getName();
					if(t.isPrimitive()){
						isMult=false;
					}else{
						String name = t.getName();
						if(name.indexOf("java.lang")>=0){
							isMult=false;
						}
						else if(name.indexOf(BigDecimal.class.getName())>=0|| name.indexOf(BigInteger.class.getName())>=0|| name.indexOf(BigInteger.class.getName())>=0){
							isMult=false;
						}
					}
					if(isMult){
						analyze(t);
						if(map.containsKey(t)){
							AnalyzeResult analyzeResult = map.get(t);
							List<String> fldns = analyzeResult.getFieldNames();
							if(fldns!=null && !fldns.isEmpty()){
								idFieldNames=new String[fldns.size()];
								idFieldNames=fldns.toArray(idFieldNames);
							}
						}
					}
					break;
				}
			}
		}
	}
	AnalyzeResult analyzeResult = new AnalyzeResult(fieldNames, idField, idClass, idFieldNames);
	analyzeResult.setReadMethod(readMethod);
	analyzeResult.setWriteMethod(writeMethod);
	map.put(clazz, analyzeResult);
 }
 
 public static class AnalyzeResult implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -6562946618321844998L;
	private Map<String,Method> readMethod=null;
	private Map<String,Method> writeMethod=null;
	List<String> fieldNames=null;
	private String idFieldName=null;
	private Class<?> idClass=null;
	private String[] idFieldNames=null;
	 
	public AnalyzeResult(List<String> fieldNames, String idFieldName,
			Class<?> idClass, String[] idFieldNames) {
		super();
		this.fieldNames = fieldNames;
		this.idFieldName = idFieldName;
		this.idClass = idClass;
		this.idFieldNames = idFieldNames;
	}
	
	public List<String> getFieldNames() {
		return fieldNames;
	}

	public String getIdFieldName() {
		return idFieldName;
	}
	public Class<?> getIdClass() {
		return idClass;
	}
	public String[] getIdFieldNames() {
		return idFieldNames;
	}
	public Map<String, Method> getReadMethod() {
		return readMethod;
	}
	public void setReadMethod(Map<String, Method> readMethod) {
		this.readMethod = readMethod;
	}
	public Map<String, Method> getWriteMethod() {
		return writeMethod;
	}
	public void setWriteMethod(Map<String, Method> writeMethod) {
		this.writeMethod = writeMethod;
	}
	 
 }
}
